<?php

namespace App\Libraries;

class Utility
{
    private static $request_id = null;

    private static $is_log_sys_info = null;

    /**
     * 生成RequestId
     */
    public static function getRequestId()
    {
        if (!empty(self::$request_id)) {
            return self::$request_id;
        }

        // 透传ID必记录
        if (
            ($request_from = array_get($_SERVER, 'HTTP_X_REQUEST_FROM'))
            && ($request_id = array_get($_SERVER, 'HTTP_X_REQUEST_ID'))
        ) {
            self::$request_id = sprintf('%s.%s', $request_from, $request_id);
        }

        if (!isset(self::$request_id)) {
            self::$request_id = self::getGuid();
        }

        return self::$request_id;
    }

    /**
     * 写入RequestId
     */
    public static function setRequestId($request_id)
    {
        if ($request_id) {
            self::$request_id = $request_id;
        }

        return self::$request_id;
    }

    /**
     * 是否记录sql详细
     */
    public static function logSql()
    {
        $config = config('log_config');
        return array_get($config, 'sql') && in_array(gethostname(), explode(',', array_get($config, 'hostnames')));
    }

    /**
     * 是否记录系统响应时间和sql详情
     */
    public static function logSysInfo()
    {
        if (!isset(self::$is_log_sys_info)) {
            // 判断概率
            $percent = config('log_config.percent');
            // 流量开关 0-99 随机数
            $rand = rand(0, 99);

            if ($rand > $percent) {
                // 不记录日志
                self::$is_log_sys_info = false;
            } else {
                self::$is_log_sys_info = true;
            }
        }

        return self::$is_log_sys_info;
    }

    /* getClientIp
     * 获取客户端Ip地址
     *
     * @static
     * @return string
     */
    public static function getClientIp()
    {
        if (getenv('HTTP_CLIENT_IP')) {
            $ip = getenv('HTTP_CLIENT_IP');
        } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
            $ip = getenv('HTTP_X_FORWARDED_FOR');
        } elseif (getenv('REMOTE_ADDR')) {
            $ip = getenv('REMOTE_ADDR');
        } else {
            $ip = '127.0.0.1';
        }

        return $ip;
    }

    /**
     * 生成签名
     */
    public static function getArgSign($params = [], $key)
    {
        //生成sign
        $sign_params = "";
        ksort($params);
        reset($params);

        foreach ($params as $k => $v) {
            if ("" != $v && "sign" != $k) {
                $sign_params .= $k . "=" . $v . "&";
            }
        }

        $sign_params .= "key=" . $key;

        $sign = strtolower(md5($sign_params));

        return $sign;
    }

    /**
     * php guid
     */
    public static function getGuid()
    {
        $char_id = strtoupper(md5(uniqid(mt_rand(), true)));

        $hyphen = chr(45);// "-"
        $uuid = substr($char_id, 0, 8) . $hyphen
            . substr($char_id, 8, 4) . $hyphen
            . substr($char_id, 12, 4) . $hyphen
            . substr($char_id, 16, 4) . $hyphen
            . substr($char_id, 20, 12);

        return $uuid;
    }

    /**
     * 拼接url
     * @param $url
     * @param null $data
     * @return string
     */
    public function getUrl($url, $data = null)
    {
        if (!is_null($data)) {
            if (is_array($data)) {
                $data = http_build_query($data);
            }

            if (strpos($url, '?') === false) {
                $url .= '?';
            }

            $url .= $data;
        }

        return $url;
    }


    /**
     * 原始数组转换为支付宝所需字符串格式
     *
     * @param string $params
     * @param string $quote
     * @static
     * @access private
     * @return string
     */
    public static function _params2String($params)
    {
        $legalStrings = [];

        ksort($params);
        reset($params);

        foreach ($params as $key => $value) {
            $legalStrings[] = $key . '=' . $value;
        }

        $string = implode('&', $legalStrings);
        if (get_magic_quotes_gpc()) {
            $string = stripslashes($string);
        }


        return $string;
    }

    /**
     * 添加签名
     *
     * @param string $string
     * @param string $quote
     * @access private
     * @return string
     */
    public static function _addSign($string, $pub_key)
    {
        $sign = '';
        $priKey = file_get_contents($pub_key);
        $res = openssl_get_privatekey($priKey);
        openssl_sign($string, $sign, $res);
        openssl_free_key($res);
        $sign = base64_encode($sign);

        return $sign;
    }

    //芝麻 公钥加密
    public static function pubEncrypt($data, $pub_key, $mode)
    {

        if (!is_string($data)) {
            return null;
        }

        $pub_key = file_get_contents($pub_key);
        $result = self::encrypted($data, $pub_key, $mode);
        return $result;

    }

    // 商户 私钥解密
    public static function priDecrypt($data, $pri_key, $mode)
    {
        if (!is_string($data)) {
            return null;
        }

        $pri_key = file_get_contents($pri_key);
        $result = self::decrypted($data, $pri_key, $mode);
        return $result;

    }

    // 加密
    public static function encrypted($data, $key, $type)
    {
        $max_length = 117;
        $output = '';
        while ($data) {
            $input = substr($data, 0, $max_length);
            $data = substr($data, $max_length);

            if ($type == 'private') {
                openssl_private_encrypt($input, $encrypted, $key);
            } else {
                openssl_public_encrypt($input, $encrypted, $key);
            }

            $output .= $encrypted;
        }

        return base64_encode($output);

    }

    //解密
    public static function decrypted($data, $key, $type)
    {
        $data = base64_decode($data);
        $max_length = 128;
        $output = '';
        while ($data) {
            $input = substr($data, 0, $max_length);
            $data = substr($data, $max_length);
            if ($type == 'private') {
                openssl_private_decrypt($input, $out, $key);
            } else {
                openssl_public_decrypt($input, $out, $key);
            }

            $output .= $out;
        }

        return $output;
    }

    public static function microtimeFloat()
    {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }

    public static function getRealSql($query, $bindings)
    {
        if (!is_array($bindings) || count($bindings) == 0) {
            return $query;
        }
        $sql = $query;
        foreach ($bindings as $val) {
            if (!is_numeric($val)) {
                $val = "'$val'";
            }
            $sql = preg_replace('/\?/', $val, $sql, 1);
        }
        return $sql;
    }

    /**
     * 判断是否Ajax请求
     */
    public static function isAjaxRequest()
    {
        if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判断是否是移动端
     */
    public static function isMobile() {

    }

    /**
     * 号码脱敏展现
     *
     * @params string  $string   需要脱敏的内容
     * @params integer $head     开头处要保留的字符串长度
     * @params integer $foot     结尾处要保留的字符串长度
     *
     * @return string
     */
    public static function deSensitive($string, $head = 1, $foot = 1)
    {
        $sensitive = strlen($string) - $head - $foot;

        if ($string && $sensitive > 0) {
            $string = substr_replace($string, str_repeat('*', $sensitive), $head, (-$foot));
        }

        return $string;
    }

    /**
     * 姓名脱密
     */
   public static function desensitize($string, $start = 0, $length = 0, $re = '*'){
       if (is_numeric($string) || preg_match("/^[^\x80-\xff]+$/", $string)) {
           $a = $string[0];
           $len = strlen($string);
           $b = $string[$len - 1];
           $_strin = '';
           for ($i = 0; $i <= $len; $i++) {
               $_strin .= "*";
           }
           return $a . $_strin . $b;
       } else{
           if (empty($string) || empty($length) || empty($re)) return $string;
           $end = $start + $length;
           $strlen = mb_strlen($string);
           $str_arr = array();
           for ($i = 0; $i < $strlen; $i++) {
               if ($i >= $start && $i < $end)
                   $str_arr[] = $re;
               else
                   $str_arr[] = mb_substr($string, $i, 1);
           }
           return implode('', $str_arr);
       }
    }

    /**
     * 计算年龄
     *
     * @params string  $from_date 开始日期
     * @params string  $to_date   截止日期
     * @return integer $age       年龄
     */
    public static function calAge($from_date, $to_date)
    {
        $from_arr = explode('-', $from_date);
        $to_arr = explode('-', $to_date);

        $age = $to_arr[0] - $from_arr[0];

        if ($age > 0) {
            if ($to_arr[1] == $from_arr[1]) {
                if ($to_arr[2] < $from_arr[2]) {
                    $age = $age - 1;
                }
            } elseif ($to_arr[1] < $from_arr[1]) {
                $age = $age - 1;
            }
        }

        return $age;
    }

    public static function checkPwd($pwd)
    {
        $pwdreg = '/(?=^.{6,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/';
        $result = preg_match($pwdreg, $pwd);

        return !empty($result);
    }

    /**
     * 一维数据数组生成数据树
     * @param array $list 数据列表
     * @param string $id 父ID Key
     * @param string $pid ID Key
     * @param string $son 定义子数据Key
     * @return array
     */
    public static function arr2tree($list, $id = 'id', $pid = 'pid', $son = 'sub')
    {
        list($tree, $map) = [[], []];
        foreach ($list as $item) {
            $map[$item[$id]] = $item;
        }
        foreach ($list as $item) {
            if (isset($item[$pid]) && isset($map[$item[$pid]])) {
                $map[$item[$pid]][$son][] = &$map[$item[$id]];
            } else {
                $tree[] = &$map[$item[$id]];
            }
        }
        unset($map);
        return $tree;
    }

    /**
     * 一维数据数组生成数据树
     * @param array $list 数据列表
     * @param string $id ID Key
     * @param string $pid 父ID Key
     * @param string $path
     * @param string $ppath
     * @return array
     */
    public static function arr2table(array $list, $id = 'id', $pid = 'pid', $path = 'path', $ppath = '')
    {
        $tree = [];
        foreach (self::arr2tree($list, $id, $pid) as $attr) {
            $attr[$path] = "{$ppath}-{$attr[$id]}";
            $attr['sub'] = isset($attr['sub']) ? $attr['sub'] : [];
            $attr['spt'] = substr_count($ppath, '-');
            $attr['spl'] = str_repeat("&nbsp;&nbsp;&nbsp;├&nbsp;&nbsp;", $attr['spt']);
            $sub = $attr['sub'];
            unset($attr['sub']);
            $tree[] = $attr;
            if (!empty($sub)) {
                $tree = array_merge($tree, self::arr2table($sub, $id, $pid, $path, $attr[$path]));
            }
        }

        return $tree;
    }

    /**
     * 获取数据树子ID
     * @param array $list 数据列表
     * @param int $id 起始ID
     * @param string $key 子Key
     * @param string $pkey 父Key
     * @return array
     */
    public static function getArrSubIds($list, $id = 0, $key = 'id', $pkey = 'pid')
    {
        $ids = [intval($id)];
        foreach ($list as $vo) {
            if (intval($vo[$pkey]) > 0 && intval($vo[$pkey]) === intval($id)) {
                $ids = array_merge($ids, self::getArrSubIds($list, intval($vo[$key]), $key, $pkey));
            }
        }
        return $ids;
    }

    public static function params2String($params)
    {
        $legalStrings = [];

        ksort($params);
        reset($params);

        foreach ($params as $key => $value) {
            if (!is_string($value) && !is_numeric($value)) { // 强制参数为数字或字符串, json不允许有二级key
                throw new \Exception('', '签名必须为字符串或数字');
            }
            $legalStrings[] = $key . '=' . $value;
        }

        $string = implode('&', $legalStrings);

        return $string;
    }

    /**
     * 处理api接口签名
     *
     * @param array $params
     * @return array
     */
    public static function buildApiSignData(array $params = []): array
    {
        $api_key = config('setting.bitdeer_api_key');
        $api_secret = config('setting.bitdeer_api_secret');

        $timestamp = time();

        $data = [
            'api_id' => $api_key,
            'timestamp' => $timestamp
        ];

        if (!empty($params)) {
            $data = array_merge($data, $params);
        }

        $dataStr = static::params2String($data);

        $sign = hash_hmac("sha256", $dataStr, $api_secret);
        $data['sign'] = $sign;

        return $data;
    }

    /**
     * @param $input
     * @return mixed
     */
    public static function xssClean($input)
    {
        $input = preg_replace('/([\x00-\x08\x0b-\x0c\x0e-\x19])/', '', $input);

        // straight replacements, the user should never need these since they're normal characters
        // this prevents like <IMG SRC=@avascript:alert('XSS')>
        $search = 'abcdefghijklmnopqrstuvwxyz';
        $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $search .= '1234567890!@#$%^&*()';
        $search .= '~`";:?+/={}[]-_|\'\\';
        for ($i = 0; $i < strlen($search); $i++) {
            // ;? matches the ;, which is optional
            // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars

            // @ @ search for the hex values
            $input = preg_replace('/(&#[xX]0{0,8}' . dechex(ord($search[$i])) . ';?)/i', $search[$i], $input); // with a ;
            // @ @ 0{0,7} matches '0' zero to seven times
            $input = preg_replace('/(&#0{0,8}' . ord($search[$i]) . ';?)/', $search[$i], $input); // with a ;
        }

        // now the only remaining whitespace attacks are \t, \n, and \r
        $ra1 = array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
        $ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
        $ra = array_merge($ra1, $ra2);

        $found = true; // keep replacing as long as the previous round replaced something
        while ($found == true) {
            $input_before = $input;
            for ($i = 0; $i < sizeof($ra); $i++) {
                $pattern = '/';
                for ($j = 0; $j < strlen($ra[$i]); $j++) {
                    if ($j > 0) {
                        $pattern .= '(';
                        $pattern .= '(&#[xX]0{0,8}([9ab]);)';
                        $pattern .= '|';
                        $pattern .= '|(&#0{0,8}([9|10|13]);)';
                        $pattern .= ')*';
                    }
                    $pattern .= $ra[$i][$j];
                }
                $pattern .= '/i';
                $replacement = substr($ra[$i], 0, 2) . '<x>' . substr($ra[$i], 2); // add in <> to nerf the tag
                $input = preg_replace($pattern, $replacement, $input); // filter out the hex tags
                if ($input_before == $input) {
                    // no replacements were made, so exit the loop
                    $found = false;
                }
            }
        }
        return $input;
    }

    public static function isHttps()
    {
        if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
            return true;
        } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
            return true;
        } elseif (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') {
            return true;
        }

        return false;
    }

}
